using System;
using System.Collections.Generic;
using System.Net;
using System.Xml;
using Freshbooks.XmlSerializers;
using RestSharp;

namespace Freshbooks
{
    public class FreshbooksApi
    {
        private readonly RestClient _client;

        public CallbackService Callback { get; private set; }
        public CategoryService Category { get; private set; }
        public ClientService Client { get; private set; }
        public EstimateService Estimate { get; private set; }
        public ExpenseService Expense { get; private set; }
        public FreshbooksSystemService System { get; private set; }
        public GatewayService Gateway { get; private set; }
        public InvoiceService Invoice { get; private set; }
        public ItemService Item { get; private set; }
        public LanguageService Language { get; private set; }
        public PaymentService Payment { get; private set; }
        public ProjectService Project { get; private set; }
        public RecurringService Recurring { get; private set; }
        public StaffService Staff { get; private set; }
        public TaskService Task { get; private set; }
        public TaxService Tax { get; private set; }
        public TimeEntryService TimeEntry { get; private set; }
        public NetworkCredential Credential { get; private set; }

        public FreshbooksApi()
        { }

        public FreshbooksApi(string AccessLink, string Token)
            : this(new Uri(AccessLink), Token)
        { }

        public FreshbooksApi(Uri AccessLink, string Token)
        {
            AccessUrl = AccessLink;
            AuthenticationToken = Token;

            _client = new RestClient(AccessUrl.ToString())
            {
                Authenticator = new HttpBasicAuthenticator(Token, "X")
            };
            _client.AddHandler("application/xml; charset=utf-8", new FreshbooksXmlDeserializer());
            _client.AddHandler("application/xml", new FreshbooksXmlDeserializer());

            Callback = new CallbackService(this);
            Category = new CategoryService(this);
            Client = new ClientService(this);
            Estimate = new EstimateService(this);
            Expense = new ExpenseService(this);
            System = new FreshbooksSystemService(this);
            Gateway = new GatewayService(this);
            Invoice = new InvoiceService(this);
            Item = new ItemService(this);
            Language = new LanguageService(this);
            Payment = new PaymentService(this);
            Project = new ProjectService(this);
            Recurring = new RecurringService(this);
            Staff = new StaffService(this);
            Task = new TaskService(this);
            Tax = new TaxService(this);
            TimeEntry = new TimeEntryService(this);
            Credential = new NetworkCredential(AuthenticationToken, "X");
        }

        protected RestClient RestClient { get { return _client; } }

        public Uri AccessUrl { get; set; }

        public string AuthenticationToken { get; set; }

        public T Execute<T>(FreshbooksRestRequest restRequest) where T : new()
        {
            RestResponse<T> response = _client.Execute<T>(restRequest);
            return response.Data;
        }

        public RestResponse Execute(FreshbooksRestRequest restRequest)
        {
            RestResponse res = _client.Execute(restRequest);
            ValidateFreshbooksRestResponse(res);
            return res;
        }

        /// <summary>
        /// Gets the Collection
        /// </summary>
        /// <returns></returns>
        public List<T> GetCollection<T>(FreshbooksRestRequest request)
        {
            return Execute<List<T>>(request);
        }

        protected void ValidateFreshbooksRestResponse(RestResponse response)
        {
            if (response.StatusCode == HttpStatusCode.NotAcceptable)
            {
                string error = "Freshbooks could not handle the input you gave it";
                try
                {
                    var doc = new XmlDocument();
                    doc.LoadXml(response.Content);
                    error = doc.DocumentElement["error"].FirstChild.Value;
                }
                catch (Exception)
                {
                }

                throw new FreshbooksNotAcceptableInputException(error);
            }
        }

        internal FreshbooksRestRequest GetRequest()
        {
            return new FreshbooksRestRequest
            {
                Credentials = Credential,
                Method = Method.POST,
                RequestFormat = DataFormat.Xml,
                XmlSerializer = new FreshbooksXmlSerializer()
            };
        }
    }
}